﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Storyteller.Framework.Graphics.Particles
{
	public class ParticleEffectManager
	{
		public static ParticleEffectManager Instance { get; private set; }

		public Random Random { get { return random; } }

		public ParticleEffectManager()
		{
			if (Instance != null)
				throw new Exception("Cannot create more than one instance of ParticleEffectManager");

			Instance = this;

			updateThread = new Thread(UpdateThread);
			updateThread.Start();

			random = RandomUtil.Random;
		}

		public void Cleanup()
		{
			exitThread = true;
			updateStartEvent.Set();
		}

		public void Add(ParticleEffect particleEffect)
		{
			effectsToAdd.Add(particleEffect);
		}

		public void Remove(ParticleEffect particleEffect)
		{
			effectsToRemove.Remove(particleEffect);
		}

		public void UpdateThread()
		{
#if XBOX
			Thread.CurrentThread.SetProcessorAffinity(new int[] { 5 });
#endif
			while (true)
			{
				updateStartEvent.WaitOne();

				if (exitThread)
					return;

				foreach (ParticleEffect effect in particleEffects)
				{
					effect.Update(deltaSeconds);
				}

				updateCompleteEvent.Set();
			}
		}

		public void BeginUpdate(float deltaSeconds)
		{
			foreach (ParticleEffect effect in effectsToAdd)
				particleEffects.Add(effect);
			foreach (ParticleEffect effect in effectsToRemove)
				particleEffects.Remove(effect);

			effectsToAdd.Clear();
			effectsToRemove.Clear();

			this.deltaSeconds = deltaSeconds;
			updateCompleteEvent.Reset();
			updateStartEvent.Set();
		}

		public void FinishUpdate()
		{
			updateCompleteEvent.WaitOne();

			particleEffects.RemoveAll(e => e.Remove);
		}

		List<ParticleEffect> particleEffects = new List<ParticleEffect>();
		List<ParticleEffect> effectsToAdd = new List<ParticleEffect>();
		List<ParticleEffect> effectsToRemove = new List<ParticleEffect>();

		Thread updateThread;
		AutoResetEvent updateStartEvent = new AutoResetEvent(false);
		ManualResetEvent updateCompleteEvent = new ManualResetEvent(false);
		bool exitThread = false;
		float deltaSeconds;
		Random random;
	}
}
